package org.traffometer.traffosim;

import java.io.IOException;
//import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;

import org.traffometer.common.data.TraffoPacket;
import org.traffometer.opendmtp.protocol.Log;
import org.traffometer.opendmtp.protocol.Packet;

/**
 * Class for establishing a simple TCP connection with a server.
 * 
 * @author Alexey Olkov
 */
public class TraffobaseConnection {
  
  /** Represents no transport. */
  public static final int TRANSPORT_NONE = 0;
  /** Represents duplex transport. */
  public static final int TRANSPORT_DUPLEX = 2;
  
  protected String hostname;
  protected int port;
//  protected TransmissionMode mode;
  protected int rate;

  private Socket socket;
  //private InputStream input = null;
  private OutputStream output;
  private static final String LOG_NAME = "SOCKETCONN";
  private int xportType = TRANSPORT_NONE;

  /**
   * Constructor.
   * 
   * @param hostname Host
   * @param port Port
   */

  public TraffobaseConnection(String hostname, int port) {
    this.hostname = hostname;
    this.port = port;
  }

  /**
   * Returns an output stream of the TCP socket.
   * @return outputstream
   */
  public OutputStream getOutputStream() {
    return this.output;
  }   
  
  /**
   * Connects using TCP.
   * 
   * @return true if connection is established; false otherwise.
   */
  public boolean connect() {
    if (this.hostname.equals("") || (port <= 0)) {
      Log.error(LOG_NAME, "Invalid 'host:port': " + this.hostname + ":" + this.port);
      return false;
    }
    try {
      Log.debug(LOG_NAME, "TCP Connect: " + this.hostname + ":" + this.port);
      this.socket = new Socket(this.hostname, this.port);
      //this.input = this.socket.getInputStream();
      this.output = this.socket.getOutputStream();
    }
    catch (UnknownHostException uhoe) { // timeout
      Log.error(LOG_NAME, "Timeout sending to TCP host: " + this.hostname + ":" + this.port);
      return false;
    }
    catch (ConnectException e) {
      Log.error(LOG_NAME, "Connection refused by TCP host: " + this.hostname + ":" + this.port);
      return false;
    }
    catch (IOException ioe) {
      Log.error(LOG_NAME, "Exception", ioe);
      return false;
    }
    this.xportType = TRANSPORT_DUPLEX;
    Packet accountIDPacket = Packet.createClientPacket(Packet.PKT_CLIENT_ACCOUNT_ID,"opendmtp");
    sendBytes(accountIDPacket.encode());
    Packet deviceIDPacket = Packet.createClientPacket(Packet.PKT_CLIENT_DEVICE_ID,"mobile");
    sendBytes(deviceIDPacket.encode());
    return isConnected();
  }

  /**
   * Disconnects from a server.
   * 
   */
  public void disconnect() {
    try {
      if (this.socket != null) {
//        Packet EobPacket = Packet.createClientPacket(Packet.PKT_CLIENT_EOB_DONE,0x0000);
//        sendBytes(EobPacket.encode());
        this.socket.close();
        this.socket = null;
      }
    }
    catch (IOException e) {
      this.socket = null;
    }
    /* clear vars */
    this.xportType = TRANSPORT_NONE;
  }
  
  
  /**
   * Checks if socket is currently bound to a server.
   * @return true if the connection is active. False otherwise.
   */
  public boolean isConnected() {
    return (this.xportType != TRANSPORT_NONE);
  }
 
  /**
   * Sends a byte buffer into an output stream of the socket.
   * @param buf Buffer
   * @return the length of the buffer sent, or -1 in case of error
   */
  protected int sendBytes(byte[] buf) {
    if (this.xportType == TRANSPORT_DUPLEX) {
      if ((this.socket == null) || (buf == null)) {
        Log.error(LOG_NAME, "Null socket/data");
        return -1;
      }
      else {
        try {
          //OutputStream out = this.socket.getOutputStream();
          this.output.write(buf);
          this.output.flush();
          return buf.length;
        }
        catch (IOException ioe) {
          Log.error(LOG_NAME, "Unable to write bytes");
          return -1;
        }
      }
    }

    /* error */
    Log.error(LOG_NAME, "Undefined transport: " + this.xportType);
    return -1;
  }
 
  /**
   * Sends a TraffoPacket.
   * @param traffoPacket a packet to be sent
   */
  public void sendPacket(TraffoPacket traffoPacket) {
   sendBytes(traffoPacket.getPacket().encode());
  } 


}
